/**
 * OSS is a Open Service Container which gives the leverage of building the Desktop and Web
 * Application. * Copyright (C) 2008-2009  Ponraj Sutanthiramani
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Or5e Open Source Community
 * Navigator Bldg, ITPL, Bangalore, India.
 */
package org.or5e.api.liveradio;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.or5e.api.mp.Settings;
import org.or5e.api.mp.player.audio.AudioPlayer;
import org.or5e.api.mp.player.audio.PlayerException;
import org.or5e.core.BaseObject;
import org.or5e.core.BaseUtils;

/**
 * This is the Default Implementation of the Live Radio.
 * @author Ponraj Suthanthriamani
 * @version 1.0b
 */
public class LiveRadioSpi extends BaseObject implements LiveRadio{
    private Map<String, RadioInfo> liveChannelDB = new HashMap<String, RadioInfo>();
    private Boolean isInitilized = Boolean.FALSE;
    private static Boolean isPlaying = Boolean.FALSE;
    private AudioPlayer _player = null;
    private BaseUtils _utils = null;
    protected LiveRadioSpi() {
        _utils = BaseUtils.getUtility();
        Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownLiveRadio()));
    }

    @Override
    public String getName() {
        return "LiveRadioSpi";
    }

    /**
     * Initilizing the Radio Channel, this needs to have the Provider name as well
     * as the Property file which has the key value pair of the Radio provider list
     *
     * @param radioProvider - This is the provider name currently we support shout
     * @param propFile - the property file which has the key and value of the url
     * @return true if it is success and false if not initilized properly.
     * @throws LiveRadioException
     */
    @Override
    public boolean initilizeRadioChannels(String radioProvider, String propFile) throws LiveRadioException {
        info("Initilizing the Default Live Radio SPI");
        LiveRadioChannelBuilder channelBuilder = new LiveRadioChannelBuilder();
        try {
            if(_utils.isEmptyString(propFile)) {
                info("*** Property file is empty so we are defaulting to the existing one.");
                propFile = "config/radioChannel.properties";
            }
            LiveRadioChannel channel = channelBuilder.getShoutChannel(propFile);
            liveChannelDB.putAll(channel.initChannels());
        } catch (IOException ex) {
            Logger.getLogger(LiveRadioSpi.class.getName()).log(Level.SEVERE, null, ex);
        }
        initDefaultPlayer();
        isInitilized = Boolean.TRUE;
        info("Initilization of Live radio is done successfully.");
        return isInitilized;
    }

    /**
     * This will return the initilized channels which is available to listen. we need
     * to provide the key when we need to play this channel.
     *
     * @return @see java.util.Set this will have the list of the Available channel to listen
     * @throws LiveRadioException
     */
    @Override
    public Set<String> getAvailableChannelList() throws LiveRadioException {
        isInitilized();
        info("Aquaring the channel list from the cached pool.");
        return liveChannelDB.keySet();
    }

    /**
     * This will start play the channel we ask for. Need to make sure you provide the
     * rite channel name so that to avoid the exceptional behaviour. you will get the
     * valid channel name using getAvailableChannelList(). if there is already a channel
     * is playing then it will stop the current playing radio and start the new one.
     * @param channelName
     * @throws LiveRadioException
     */
    @Override
    public void playChannel(String channelName) throws LiveRadioException {
        isInitilized();
        info("About to play the channel requested for :"+channelName);
        if(isPlaying) {
            info("Stopping the current playing radio.");
            _player.stop();
        }
        RadioInfo radInfo = liveChannelDB.get(channelName);
        try {
            info("Opening and playing the radio URL.");
            _player.open(radInfo.getURL());
            _player.play();
            isPlaying = Boolean.TRUE;
        }
        catch (PlayerException ex) {
            isPlaying = Boolean.FALSE;
            throw new LiveRadioException(LiveRadioExceptionConstants.E004);
        }
    }

    /**
     * This will stop the current radio channel track.
     * @param channelName
     * @throws LiveRadioException
     */
    @Override
    public void stopChannel(String channelName) throws LiveRadioException {
        isInitilized();
        if(isPlaying) {
            _player.stop();
            isPlaying = Boolean.FALSE;
        }
        else {
            throw new LiveRadioException(LiveRadioExceptionConstants.E002);
        }
    }

    /**
     * Yet to be implemented
     * @return
     * @throws LiveRadioException
     */
    @Override
    public Float increaseVolume() throws LiveRadioException {
        isInitilized();
        Float currentGain = _player.getGain();
        try {
            if(currentGain != 100.0f) {
                _player.setGain(++currentGain);
                return ++currentGain;
            }
        }
        catch (PlayerException ex) {
            throw new LiveRadioException(LiveRadioExceptionConstants.E003);
        }
        return currentGain;
    }

    /**
     * Yet to be implemted
     * @return
     * @throws LiveRadioException
     */
    @Override
    public Float decreaseVolume() throws LiveRadioException {
        throw new LiveRadioException(LiveRadioExceptionConstants.E005);
    }

    /**
     * Yet to be implemented
     * @param volumeLevel
     * @throws LiveRadioException
     */
    @Override
    public void setVolume(Float volumeLevel) throws LiveRadioException {
        isInitilized();
        if(isPlaying) {
            try {
                _player.setGain(volumeLevel);
            }
            catch (PlayerException ex) {
                throw new LiveRadioException(LiveRadioExceptionConstants.E003);
            }
        }
    }

    /**
    * Life cycle method which will deinitilize all the resource loced by this API.
    * @return
    * @throws LiveRadioException
    */
    @Override
    public boolean destroyRadioChannels() throws LiveRadioException {
        throw new LiveRadioException(LiveRadioExceptionConstants.E005);
    }

    /**
     * This will return the Channel information like, URL, Title etc
     * @param availChannelList
     * @return
     * @throws LiveRadioException
     */
    @Override
    public RadioInfo getChannelInfo(String availChannelList) throws LiveRadioException{
        isInitilized();
        return liveChannelDB.get(availChannelList);
    }

    /**
     * If you want to set your own Audio player configuration. you can created your
     * own audio player and configure of your own desire and set it to this component
     * @param audioPlayer
     */
    @Override
    public void setAudioComponent(AudioPlayer audioPlayer) {
        if(this._player != null) {
            this._player.stop();
            this._player = null;
        }
        this._player = audioPlayer;
    }

    private void isInitilized() throws LiveRadioException {
        if( ! isInitilized || liveChannelDB.size() == 0 ) throw new LiveRadioException(LiveRadioExceptionConstants.E001);
    }
    private void initDefaultPlayer() {
        Settings.loadSettings();
        String mixerName = Settings.getMixerName();
        _player = new AudioPlayer();

        if(mixerName != null) {
            _player.setMixerName(mixerName);
        }
    }
    class ShutdownLiveRadio implements Runnable {

        @Override
        public void run() {
            info("Shutting down the Live Radio.");
            if(isPlaying) {
            	_player.stop();
            }
        }
    }
}